package utils;

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
import utils.tcp.Modbus4jUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class TCPUtils {

    /**
     * 根据指定IP及端口获取ModbusMaster
     *
     * @param IP   PLC对应IP
     * @param port PLC开放端口
     * @return ModbusMaster
     */
    public static ModbusMaster getMaster(String IP, Integer port) {
        ModbusMaster master;
        try {
            master = Modbus4jUtils.getMaster(IP, port);
        } catch (ModbusInitException e) {
            return null;
            // throw new RuntimeException(e);
        }
        return master;
    }

    public static Number getPLCData(ModbusMaster master, int slaveId, int startAddress, int dataType)
            throws ModbusTransportException, ErrorResponseException {
        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, startAddress, dataType);
        return master.getValue(loc);
    }

    /**
     * 获取PLC指定地址的报文解析内容
     *
     * @param master       传入modbusMaster
     * @param slaveId      对应slaveId
     * @param startAddress 开始地址值
     * @param Long         地址长度
     * @return 返回解析到的List Integer
     */
    public static List<Integer> getPLCCurve(ModbusMaster master, int slaveId,
                                            int startAddress, int Long) {
        int count = 100; // 读取寄存器的数量
        List<Integer> list = new ArrayList<>();
        int num = Long / 100;
        int countForBreak = 0;
        for (int i = 0; i < num; i++) {
            countForBreak = getCurve(master, slaveId, startAddress, count, list, i);
            if (countForBreak >= 3) {
                break;
            }
        }
        if (Long % 100 != 0 && countForBreak < 3) {
            int m = Long % 100;
            getCurve(master, slaveId, startAddress, m, list, 1);
        }
        return list;
    }

    public static List<Float> getPLCCurveFloat(ModbusMaster master, int slaveId,
                                               int startAddress, int Long) {
        int count = 100; // 读取寄存器的数量
        List<Float> list = new ArrayList<>();
        int num = Long / 100;
        int countForBreak = 0;
        for (int i = 0; i < num; i++) {
            countForBreak = getFloatCurve(master, slaveId, startAddress, count, list, i);
            if (countForBreak >= 3) {
                break;
            }
        }
        if (Long % 100 != 0 && countForBreak < 3) {
            int m = Long % 100;
            getFloatCurve(master, slaveId, startAddress, m, list, 1);
        }
        return list;
    }

    /**
     * 获取曲线
     *
     * @param master       ModbusMaster
     * @param slaveId      slaveId
     * @param startAddress 起始地址
     * @param count        一次性读取寄存器数量
     * @param list         存入的list
     * @param i            执行的次数
     */
    private static int getCurve(ModbusMaster master, int slaveId, int startAddress,
                                int count, List<Integer> list, int i) {
        int numberOfLongs = count / 2; // 根据寄存器数量计算long值的数量
        // 定义提前终端计数器
        AtomicInteger countForBreak = new AtomicInteger();
        startAddress = startAddress + i * count;
        try {
            // 创建读取请求，从指定地址开始读取指定数量的寄存器
            ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, startAddress, count);
            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
            if (!response.isException()) {
                short[] registers = response.getShortData();
                for (int j = 0; j < numberOfLongs; j++) {
                    int index = j * 2; // 计算long值在寄存器数组中的起始位置
                    long valueAsLong = ((long) registers[index + 1] << 16) | (registers[index] & 0xFFFFL);
                    list.add((int) valueAsLong);
                    if (valueAsLong == 0) {
                        countForBreak.incrementAndGet();
                    } else {
                        countForBreak.set(0);
                    }
                }
            }
        } catch (ModbusTransportException e) {
            e.printStackTrace();
        }
        return countForBreak.intValue();
    }

    private static int getFloatCurve(ModbusMaster master, int slaveId, int startAddress,
                                     int count, List<Float> list, int i) {
        int numberOfLongs = count / 2; // 根据寄存器数量计算long值的数量
        // 定义提前终端计数器
        AtomicInteger countForBreak = new AtomicInteger();
        startAddress = startAddress + i * count;
        try {
            // 创建读取请求，从指定地址开始读取指定数量的寄存器
            ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, startAddress, count);
            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
            if (!response.isException()) {
                short[] registers = response.getShortData();
                for (int j = 0; j < numberOfLongs; j++) {
                    int index = j * 2; // 计算long值在寄存器数组中的起始位置
                    int valueAsInt = (registers[index + 1] << 16) | (registers[index] & 0xFFFF);
                    float floatValue = Float.intBitsToFloat(valueAsInt);
                    list.add(floatValue);
                    if (floatValue == 0f) {
                        countForBreak.incrementAndGet();
                    } else {
                        countForBreak.set(0);
                    }
                }
            }
        } catch (ModbusTransportException e) {
            e.printStackTrace();
        }
        return countForBreak.intValue();
    }
}
